package com.prpat;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Scanner;
import java.util.regex.MatchResult;

import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import de.uniba.wiai.lspi.chord.com.local.Registry;
import de.uniba.wiai.lspi.chord.console.command.Exit;
import de.uniba.wiai.lspi.chord.console.command.JoinNetwork;
import de.uniba.wiai.lspi.chord.console.command.LeaveNetwork;
import de.uniba.wiai.lspi.chord.console.command.RemoteChordNetworkAccess;
import de.uniba.wiai.lspi.chord.console.command.ShowEntriesNetwork;
import de.uniba.wiai.lspi.chord.console.command.ShowFingerTableNetwork;
import de.uniba.wiai.lspi.util.console.Command;
import de.uniba.wiai.lspi.util.console.CommandFactory;
import de.uniba.wiai.lspi.util.console.ConsoleException;
import de.uniba.wiai.lspi.util.console.parser.TokenMgrError;

public class TaskAssistant 
{
	public static CommandFactory factory;
	
	public static final int CHAT_PORT = 5000;
	public static final int CHORD_PORT = 4242;
	
	public static int CLIENT_COUNT = 0;

	public static boolean CONNECTED;
	public static final String LOCAL_IP = getLocalHost();
	public static final String BOOTSTRAP;
	
	public static HashMap<Integer, Range> rangeMap;
	
	public static JFrame connectFrame;
	public static JFrame dialogFrame;
	
	public static JTextArea display;
	public static JTextField input;
	
	static
	{
		BOOTSTRAP = System.getProperty("com.prpat.Globals.bootstrap");
	}
	
	/**Kills all processes using the designated ports.
	 * Then creates a command console factory that will be used to interact with 
	 * Chord network 
	 *  
	 */
	public static void initializer()
	{
		suicider();
		
		Hashtable<String, String> commandMapping = new Hashtable<String, String>();
		commandMapping.put(Exit.COMMAND_NAME, Exit.class.getName());

		/*
		 * Commands to create a node that connects to a remote chord network.
		 */
		commandMapping.put(JoinNetwork.COMMAND_NAME, JoinNetwork.class.getName());
		commandMapping.put(LeaveNetwork.COMMAND_NAME, LeaveNetwork.class.getName());
		commandMapping.put(ShowEntriesNetwork.COMMAND_NAME, ShowEntriesNetwork.class.getName());
		commandMapping.put(ShowFingerTableNetwork.COMMAND_NAME, ShowFingerTableNetwork.class.getName());
	
		/*
		 * Command for personal use
		 */
		commandMapping.put(FindSuccessor.COMMAND_NAME, FindSuccessor.class.getName());
		/*
		 * Get the registry for thread communication as this is the object
		 * commands need to be executed
		 */
		Object toCommand = Registry.getRegistryInstance();

		factory = new CommandFactory(new Object[] { toCommand,
				RemoteChordNetworkAccess.getUniqueInstance() }, System.out,
				commandMapping);
	}
	
	/**
	 * Execute a command trough Unix terminal<br>
	 * <b>E.g </b>: in order to execute the command <code>ps aux | grep STRING</code> 
	 * the method should be called this way<br> 
	 * <code>terminalExec(new String[]{"ps", "aux", "|", "grep", "STRING"});</code>
	 * @usage 
	 * @param params - params[0] is the command name, rest are command parameters
	 */
	public static String terminalExec(String[] params)
    {
            String output = "";
            try 
            {
                    Process child = Runtime.getRuntime().exec( params );

                    // Get the input stream and read from it the command output
                    InputStream in = child.getInputStream();
                    int c;
                    while ((c = in.read()) != -1) 
                    {
                            output = output + (char)c;
                    }
                    in.close();
            } 
            catch (IOException e) {System.err.println("Globals, terminalExec, ");}
            return output;
            
    }
	
	/**
	 * kills all the processes using ports 4242 and 5000
	 */
	public static void suicider()
	{
		terminalExec(new String[]{"fuser", "4242/tcp", "-k"});
		terminalExec(new String[]{"fuser", "5000/tcp", "-k"});
	}
	
	public static boolean ocExecuted(String command) 
	{
		boolean executed = false;
		try 
		{
			if (command == null) 
				command = "";
			
			command = command.trim();
			
			Command com = TaskAssistant.factory.createCommand(command);
			com.execute();
			executed = true;
		} 
		catch (ConsoleException e) {
			System.err.println("Starter, ocExecuted, ConsoleException");
			System.err.println(e.getMessage());
		}
		catch (TokenMgrError tme) {
			System.err.println("Starter, ocExecuted, TokenMgrError");
			System.err.println(tme.getMessage());
		}
		catch (Throwable t) {
//			t.printStackTrace();
			System.err.println("Starter, ocExecuted, Throwable");
			System.err.println(t.getMessage());
		}
		return executed;
	}
	
	public static boolean startChord()
	{
		boolean connected = false;
		if(LOCAL_IP.equals(BOOTSTRAP))
			connected = ocExecuted("joinN");
		else
			connected = ocExecuted("joinN -bootstrap " + BOOTSTRAP);
		if(connected)
		{
			Thread serverThread = new Thread(new Server());
			serverThread.setName("PRPAT_Server_thread");
			serverThread.start();
		}
		
		return connected;
	}
	
	public static void sendMessage() 
	{
		try
		{
			  // Open the file that is the first command line parameter
			  FileInputStream fstream = new FileInputStream("src/hashes");
			  // Get the object of DataInputStream
			  DataInputStream in = new DataInputStream(fstream);
			  BufferedReader br = new BufferedReader(new InputStreamReader(in));
			  String strLine;
			  //Read File Line By Line
			  while ((strLine = br.readLine()) != null)   
			  {
				  sendMessage(strLine);
			  }
			  //Close the input stream
			  in.close();
		}
		catch (Exception e)
		{
			System.err.println("Globals, sendMessage, " + e.getMessage());
		}	
	}
	
	public static void sendMessage(String str)
	{
		Message mes = new Message(str, "range", Message.REQUEST);
		Thread client = new Thread(new Client(mes));
		client.setName("PRPAT-Client # "+(CLIENT_COUNT));
		client.start();
	}
	
    /**
     * 
     * @return the IP address of the Linux machine running this program
     * @throws java.net.SocketException
     */
    private static String getLocalHost()
    {
		Enumeration<?> en;
		try 
		{
			en = java.net.NetworkInterface.getNetworkInterfaces();
		} catch (SocketException e) {
			e.printStackTrace();
			return "";
		}
        while (en.hasMoreElements()) 
        {
            java.net.NetworkInterface i = (java.net.NetworkInterface) en.nextElement();
            for (Enumeration<InetAddress> en2 = i.getInetAddresses(); en2.hasMoreElements();)
            {
            	java.net.InetAddress addr = (java.net.InetAddress) en2.nextElement();
                if (!addr.isLoopbackAddress()) 
                {
                    if (addr instanceof java.net.Inet4Address) 
                    {
                        return addr.getHostAddress();
                    }
                }
            }
        }
        return "";
    }

    private static void parser()
    {
    	String input1 = "lvl=100 (1818 KB for nbparts) 11 G possible passwords (11461)";

    	Scanner s1 = new Scanner(input1);

    	s1.findInLine("lvl=(\\d+) \\((\\d+) KB for nbparts\\) (\\d+) [KMG] possible passwords \\((\\d+)\\)");
	     
    	MatchResult result1 = s1.match();
	    for (int i=1; i<=result1.groupCount(); i++)
	    	System.out.println(result1.group(i));
	     
	     s1.close();
    }
}
